<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
</html>
<script>

//组合模式  
        //组合模式介绍
        //举一个简单的例子，就像家里每个电器都有单独的开关，而组合模式就是设置一个总开关，这个开关可以控制家中所有电器的开关,这就是组合模式。
        //实现思想 
        /*
            先定义控制不同电器的开关，也就是一个个构造函数,每一个构造函数都有一个启动方法
        每一个构造函数的实例就是一个个电器开关
        再定义一个总开关，一个构造函数，有一个方法可以启动所有构造函数的,
        这时，需要一个承载所有构造函数实例的数组
        总开关构造函数需要一个方法, 向数组里面添加内容
        总开关构造函数需要一个方法, 能把数组里面的所有内容启动了
        */

        //组合模式代码 
           // 一个构造函数的启动方式  控制玩的开关
            class Play {
            constructor () {}

            // 这个构造函数的启动方法
            init () {
                console.log('开始玩游戏')
                this.a()
                this.b()
                this.c()
            }

            a () {}

            b () {}

            c () {}
            }

            // 第二个构造函数  控制吃的开关
            class Eat {
            constructor () {}

            init () {
                console.log('去吃饭')
            }
            }
            //第三个构造函数  控制睡觉的开关
            class Sleep {
            constructor () {}

            init () {
                console.log('去睡觉')
            }
            }


            // 组合模式的代码   总开关
            class Compose {
            constructor () {
                // 用来承载每一个实例的数组
                this.composeArr = []
            }

            // 向数组里面添加内容
            add (instance) {
                this.composeArr.push(instance)
            }

            // 把数组里面的每一个内容调用了
            init () {
                console.log('总开关启动了')
                this.composeArr.forEach(item => item.init())
            }
            }

            // c 就是一个总开关
            var c = new Compose()
            // 每一次执行 add 方法就是向总开关上添加内容   使得总开关 可以控制 其他开关
            c.add(new Play())
            c.add(new Eat())
            c.add(new Sleep())

            // 只要我的总开关一启动
            // 里面的每一个构造函数就同时都启动了
            c.init()
            console.log(c)

        //组合模式的简单应用场景
        /*
            一个页面上有许多轮播图，当我切换其他标签页面时，这个页面上的轮播图还在运行，定时器没有停止
        设置离开这个页面时，定时器停止，轮播图停止运动
        这时就可以利用组合模式，设置一个总开关，控制所有轮播图的停止与运动。
        */



//观察者模式 
        //观察者模式介绍
        /*
            观察者观察着被观察者只要被观察者数据发生变化，观察者就要做一些事情
        举个生动的例子，学生就是被观察者，老师就是观察者，只要学生上课状态不好，老师就会请家长。
        */

        //观察者模式介绍实现思想
        /*
            学生刚开始需要一个认真学习的状态
        当学生认真学习的状态改变，在上课吃辣条时
        老师观察到这个变化，就会请家长
        学生需要一个状态改变的方法，一个添加观察者老师的方法，还要有一个通知观察者自己状态改变的方法，并且再状态改变后立即让观察者老师 叫家长。
        */

        //观察者模式代码
        // 观察者  模式
        // => 让 观察者 看着 被观察者, 只要数据改变了
        // => 就让 观察者 做一些事情
        // observers  观察者


       // 被观察者
        class student {
            constructor(name) {
                this.name = name
                //最开始的状态
                this.state='学习'
                // 数组 中放入观察者  一旦被观察者状态发生改变  就让 观察者 做一些事情
                this.observers=[]
            } 
            // 改变状态
            setState(state){
                this.state=state
                //只要状态发生变化 通知观察者  数据改变了
                this.noticy()
            }
            // 获取状态
            getState(){
                return this.state
            }
            // 添加  观察者
            attach(observer){
                this.observers.push(observer)
            } 
            // 通知  观察者 => 被观察者  状态改变了
            noticy(){
                this.observers.forEach(item=>item.jiaojiazhang(this.name,this.state))
            }

        }
        // 观察者
        class teacher{
            constructor(name){
                this.name=name
            }

            jiaojiazhang(name,state){
                console.log(`我是  ${this.name}  因为  ${name}  在  ${state},我要叫家长 `)
            }
        }
        // 被观察者
        let s=new student('小明')
        console.log(s)//student {name: "小明", state: "学习", observers: Array(0)}
        // 添加  观察者
        let ob1=new teacher('班主任')
        let ob2=new teacher('教导主任')
        console.log(ob1)
        console.log(ob2)
        s.attach(ob1)
        s.attach(ob2)
        //先获取最开始的状态
        console.log(s.getState())
		// 状态改变    改变后观察者 直接执行方法
        s.setState('上课吃辣条')
        //状态改变后的状态
        console.log(s.getState())


//发布/订阅模式 
        //发布/订阅模式介绍及实现思想 
        //分成三个状态   
        //  1订阅  2取消订阅  3发布

        //要实现订阅/取消订阅功能需要一个消息盒子{ }
        /*
            订阅就是往消息盒子里添加内容
        取消订阅就是删除消息盒子里的内容
        发布就是执行消息盒子里的内容
            */

        //发布/订阅模式代码
        //   准备几个 事件处理函数  要往消息盒子里添加的内容
        function handlerA(e) { console.log('我是事件处理函数 handlerA', e) }
        function handlerB(e) { console.log('我是事件处理函数 handlerB', e) }
        function handlerC(e) { console.log('我是事件处理函数 handlerC', e) }
        function handlerD(e) { console.log('我是事件处理函数 handlerD', e) }
        function handlerE(e) { console.log('我是事件处理函数 handlerE', e) }
        class Observer {
            constructor() {
                //  准备好的消息盒子
                this.message = {}
            }
            // 订阅的方法
            on(type, fn) {
                // type  要订阅 事件类型   fn  事件处理函数
                // 如果  message 盒子中  没有  type  就添加
                // 如果  message 盒子中  有  type  就添加 事件函数
                if (!this.message[type]) {
                    this.message[type] = []
                }
                this.message[type].push(fn)
            }
            // 取消订阅的方法
            off(type, fn) {
                // 删除消息盒子里面的某一个成员
                // type  要取消的 事件类型   fn  事件处理函数
                // 如果  message 盒子中  没有  type 不操作
                // 如果  message 盒子中  有  type  删除
                if (!this.message[type]) return
                this.message[type] = this.message[type].filter(item => item != fn)
            }
            // 发布的方法
            emit(type, ...arg) {
                // 没有 订阅过 type  不操作
                //      订阅过 type  执行
                 if (!this.message[type]) return
                // 自己组装一个事件对象
                let event = {
                    type: type,
                    data: arg
                }
                this.message[type].forEach(element => element(event))
            }

        }
        let ob = new Observer()
        console.log('订阅   发布')
        ob.on('click', handlerA)
        ob.on('click', handlerB)
        ob.on('click', handlerC)
        ob.on('abc', handlerD)
        ob.on('abc', handlerE)
        ob.emit('click', 100, true, { name: '小明' }, [1, 2, 3, 4])
        ob.emit('abc', 200, true, { name: '小红' }, [1, 2, 3, 4])
        console.log('')
        console.log('取消订阅  handlerA handlerD 然后  发布')
        ob.off('click', handlerA)
        ob.off('abc', handlerD)
        ob.emit('click', false,{name:'小明'})
        ob.emit('abc',false,{name:'小红'})











</script>
